package com.rc.saas.mini.controller.recharge;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.rc.saas.base.MinipBaseController;
import com.rc.saas.mini.client.ChargerClientRouter;
import com.rc.saas.mini.common.base.DataTResponse;
import com.rc.saas.mini.common.constant.WebConstant;
import com.rc.saas.mini.common.utils.ValidationUtil;
import com.rc.saas.mini.component.UidGenerator;
import com.rc.saas.mini.minip.vo.LoginInfoVo;
import com.rc.saas.mini.model.member.MemberInfo;
import com.rc.saas.mini.model.rc.RcOperatorAccountRecord;
import com.rc.saas.mini.model.recharge.RechargeOrder;
import com.rc.saas.mini.model.recharge.RechargeOrderExample;
import com.rc.saas.mini.model.tenant.Tenant;
import com.rc.saas.mini.service.member.MemberInfoService;
import com.rc.saas.mini.service.member.MemberTicketService;
import com.rc.saas.mini.service.rc.RcOperatorAccountRecordService;
import com.rc.saas.mini.service.recharge.RechargeOrderService;
import com.rc.saas.mini.service.tenant.TenantPaymentService;
import com.rc.saas.mini.service.tenant.TenantService;
import com.rc.saas.mini.service.tenant.TenantSettingService;
import com.rc.saas.payment.alipay.config.AlipayConfig;
import com.rc.saas.payment.alipay.vo.AlipayNotifyVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 同步通知验签：
 * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号；
 * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额）；
 * 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）；
 * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明同步校验结果是无效的，只有全部验证通过后，才可以认定买家付款成功。
 */
@Controller
@RequestMapping("/alipay")
@Api(tags = "12.充值.APP.支付宝（需支付测试）")
public class AlipayController extends MinipBaseController {
    private static Logger logger = LogManager.getLogger(AlipayController.class);

    //@Value("${alipay.notifyUrl}")
    //private String notifyUrl;

    final String FAIL = "FAIL";
    final String SUCCESS = "SUCCESS";
    final String TRADE_SUCCESS = "TRADE_SUCCESS";
    final String SERVER_URL = "https://openapi.alipay.com/gateway.do";

    @Autowired
    private UidGenerator uidGenerator;

    @Autowired
    private TenantPaymentService tenantPaymentService;

    @Autowired
    private MemberInfoService memberInfoService;

    @Autowired
    private RechargeOrderService rechargeOrderService;

    @Autowired
    private TenantSettingService tenantSettingService;

    @Autowired
    private ChargerClientRouter chargerClientRouter;

    @Autowired
    private MemberTicketService memberTicketService;

    @Autowired
    private TenantService tenantService;

    @Value("${ali.appid}")
    private String aliAppid;

    @Value("${ali.privateKey}")
    private String aliPrivateKey;

    @Value("${ali.publicKey}")
    private String aliPublicKey;

    @Value("${ali.mchid}")
    private String aliMchid;

    @Value("${weChat.baseUrl}")
    private String baseUrl;
    @Autowired
    private RcOperatorAccountRecordService rcOperatorAccountRecordService;


    /**
     * 执行支付宝支付预下单,生成订单号返回给前端
     *
     * @param request
     * @param totalMoney 单位为元
     * @return
     */
    @RequestMapping(value = "/alipayPrePay", method = RequestMethod.POST)
    @ResponseBody
    @ApiOperation(value = "执行支付宝支付预下单(需要登录)", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "totalMoney", paramType = "query", value = "金额(元)", required = true)})
    public DataTResponse<String> alipayPrePay(HttpServletRequest request, String totalMoney) {

        LoginInfoVo loginInfo = readLoginInfo(request);
        if (loginInfo == null) {
            return DataTResponse.FAIL_LOGIN();
        }
        if (!ValidationUtil.checkAmount(totalMoney)) {
            return DataTResponse.FAIL("金额不合法");
        }
        logger.info("controller.alipayPrePay request:{}", totalMoney);
        BigDecimal b = new BigDecimal(totalMoney);
        b = b.setScale(2, BigDecimal.ROUND_HALF_UP); //四舍五入zhi

        totalMoney = b.toString();
        //TenantSettingExample tenantSettingExample = new TenantSettingExample();
        //tenantSettingExample.createCriteria().andTenantCodeEqualTo(loginInfoVo.getTenantCode());
        //TenantSetting tenantSetting = tenantSettingService.selectByExample(tenantSettingExample).stream().findFirst().orElseGet(() -> null);
        //if (tenantSetting == null) {
        //    return TResponse.FAIL("未配置运营商参数信息");
        //}
        //BigDecimal memberRechargeAmountMin = new BigDecimal(1);
        //memberRechargeAmountMin = tenantSetting.getMemberRechargeAmountMin() != null ? BigDecimal.valueOf(tenantSetting.getMemberRechargeAmountMin()) : memberRechargeAmountMin;
        //if ((b.multiply(new BigDecimal(100))).compareTo(memberRechargeAmountMin) == -1) {
        //    return TResponse.FAIL("最低充值" + memberRechargeAmountMin.divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN) + "元");
        //}

        //TenantPayment tenantPayment = tenantPaymentService.selectByTenantCodeAndType(redisTenantConfig.getTenantCode(), AlipayConfig.PAYMENT_TYPE);
        //if (tenantPayment == null || StringUtils.isBlank(tenantPayment.getNotifyUrl1())) {
        //    return TResponse.FAIL("未配置支付宝信息");
        //}

        MemberInfo member = memberInfoService.selectByPrimaryKey(loginInfo.getMemberId());
        if (member == null) {
            return DataTResponse.FAIL_LOGIN();
        }

        if (!"0".equals(member.getStatus())) {
            return DataTResponse.FAIL("会员已被冻结");
        }

        if (StringUtils.isBlank(member.getMobile())) {
            return DataTResponse.FAIL("未绑定手机号!");
        }
        Tenant tenant = tenantService.selectByTenantCode(member.getTenantCode());
        if (tenant == null) {
            return DataTResponse.FAIL("未配置商户信息");
        }

        //实例化客户端
        AlipayClient alipayClient = new DefaultAlipayClient(SERVER_URL,
                aliAppid, aliPrivateKey, AlipayConfig.FORMAT, AlipayConfig.CHARSET, aliPublicKey, AlipayConfig.SIGN_TYPE);

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.open.public.template.message.industry.modify
        //AlipayOpenPublicTemplateMessageIndustryModifyRequest request = new AlipayOpenPublicTemplateMessageIndustryModifyRequest();

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest alipayTradeAppPayRequest = new AlipayTradeAppPayRequest();

        String subject = tenant.getTenantName();
        String product_code = "QUICK_MSECURITY_PAY";

        //根据不同的运营商简码生成充值订单号
        String outTradeNo = uidGenerator.genAliRechargeOutTradeNo(tenant.getTenantCode(), member.getMobile());


        ///生成充值订单
        RechargeOrder rechargeOrder = new RechargeOrder();
        //充值方式  0 公众号 1 APP  2小程序 默认0
        rechargeOrder.setRechargeType(WebConstant.CON1);

        //支付方式 0:微信支付 1:支付宝
        rechargeOrder.setPaymentType(WebConstant.CON1);
        rechargeOrder.setOutTradeNo(outTradeNo);
        rechargeOrder.setAmount(new BigDecimal(totalMoney));
        rechargeOrder.setFactAmount(new BigDecimal(totalMoney));
        rechargeOrder.setMemberId(member.getMemberId());
        rechargeOrder.setStatus(WebConstant.CON0);
        rechargeOrder.setCreatedTime(new Date());
        rechargeOrder.setTenantCode(member.getTenantCode());

        //是否符合支付减免
        //BigDecimal factAmount = rechargeOrder.getFactAmount();
        //MarketActivityTicket marketActivityTicket = memberTicketService.selectCurrentReductionActivity(member, rechargeOrder);
        //if (marketActivityTicket != null) {
        //    factAmount = factAmount.subtract(new BigDecimal(marketActivityTicket.getSendAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        //    if (factAmount.compareTo(BigDecimal.ZERO) == 1) {
        //        logger.info("营销活动==》充值减免:充值金额{}，到账金额{}，减免金额{}", factAmount, rechargeOrder.getAmount(), marketActivityTicket.getSendAmount());
        //        rechargeOrder.setFactAmount(factAmount);
        //    }
        //}


        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(subject);
        model.setOutTradeNo(outTradeNo);
        model.setTotalAmount(rechargeOrder.getFactAmount().toString());
        model.setProductCode(product_code);

        //公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数
        String passback_params = String.valueOf(member.getMemberId());

        try {
            //URL编码，支付宝需要以这样的方式传递参数
            passback_params = URLEncoder.encode(passback_params, "UTF-8");
            model.setPassbackParams(passback_params);
            logger.info("参数:{}", model);
            alipayTradeAppPayRequest.setBizModel(model);
            alipayTradeAppPayRequest.setNotifyUrl(baseUrl + "/alipay/open/alipayNotify");

            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(alipayTradeAppPayRequest);
            if (response.isSuccess()) {
                rechargeOrderService.insertSelective(rechargeOrder);
                logger.info("支付宝支付预下单成功:{}", response.getBody());
                //TResponse tResponse = TResponse.SUCCESS();
                //tResponse.put(response.getBody());
                RcOperatorAccountRecord rcOperatorAccountRecord = new RcOperatorAccountRecord();
                rcOperatorAccountRecord.setRecordType("0");
                rcOperatorAccountRecord.setExplains("充值");
                rcOperatorAccountRecord.setAmount(new BigDecimal(totalMoney));
                rcOperatorAccountRecord.setRecordSource("2");
                rcOperatorAccountRecord.setEntryId(member.getMemberId());
                rcOperatorAccountRecord.setEntryName(member.getNickname());
                rcOperatorAccountRecord.setEntryType("1");
                rcOperatorAccountRecord.setEntryAccount(new BigDecimal(totalMoney));
                rcOperatorAccountRecord.setOutId(1L);
                rcOperatorAccountRecord.setOutName("平台");
                rcOperatorAccountRecord.setOutType("0");
                rcOperatorAccountRecord.setStatus("1");
                rcOperatorAccountRecord.setCreatedTime(new Date());
                rcOperatorAccountRecord.setLastUpdatedTime(new Date());
                rcOperatorAccountRecord.setRemark("充值");
                rcOperatorAccountRecordService.insertSelective(rcOperatorAccountRecord);
                return DataTResponse.SUCCESS_DATA(response.getBody());
            } else {
                logger.error("支付宝预下单失败");
                return DataTResponse.FAIL("支付宝预下单失败");
            }
        } catch (Exception e) {
            logger.error("支付宝预下单失败", e);
            return DataTResponse.FAIL("支付宝预下单失败");
        }
    }

    /**
     * https://docs.open.alipay.com/204/105301/
     * 由于同步通知和异步通知都可以作为支付完成的凭证，且异步通知支付宝一定会确保发送给商户服务端。
     * 为了简化集成流程，商户可以将同步结果仅仅作为一个支付结束的通知（忽略执行校验），实际支付是否成功，完全依赖服务端异步通知。
     * 程序执行完后必须打印输出“success”（不包含引号）。如果商户反馈给支付宝的字符不是success这7个字符，
     * 支付宝服务器会不断重发通知，直到超过24小时22分钟。一般情况下，25小时以内完成8次通知（通知的间隔频率一般是：4m,10m,10m,1h,2h,6h,15h）；
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/open/alipayNotify", method = RequestMethod.POST)
    @ResponseBody
    @ApiIgnore
    public synchronized String alipayNotify(HttpServletRequest request, HttpServletResponse response) {
        //RedisTenantConfig redisTenantConfig = readTenantInfo(request);
        //if (redisTenantConfig == null || StringUtils.isBlank(redisTenantConfig.getTenantCode())) {
        //    logger.error("找不到对应运营商信息:{}", redisTenantConfig);
        //    return FAIL;
        //}

        String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
                + request.getServletPath();

        logger.info("alipayNotify入帐通知:{}", url);

        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();

        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";

            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }

            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        logger.info("controller.alipayNotify request:{}", params);

        //rsaCheckV1会删除这两个key，需手工补上
        String sign_type = params.get("sign_type");
        String sign = params.get("sign");

        //TenantPayment tenantPayment = tenantPaymentService.selectByTenantCodeAndType(redisTenantConfig.getTenantCode(), AlipayConfig.PAYMENT_TYPE);

        try {
            //开放平台SDK提供了AlipaySignature.rsaCheckV1()与AlipaySignature.rsaCheckV2()两个版本。
            // V1验签时会剔除sign_type参数，V2会保留sign_type参数。V2仅适用于生活号接口
            boolean flag = AlipaySignature.rsaCheckV1(params, aliPublicKey, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
            if (!flag) {
                logger.error("alipay 支付异步通知验签失败");
                return FAIL;
            }
        } catch (Exception e) {
            logger.error("alipay 支付异步通知验签失败", e);
            return FAIL;
        }

        if (TRADE_SUCCESS.equals(params.get("trade_status"))) {
            //付款金额
            String amount = params.get("buyer_pay_amount");
            //商户订单号
            String out_trade_no = params.get("out_trade_no");
            //支付宝交易号
            String trade_no = params.get("trade_no");

            params.put("sign_type", sign_type);
            params.put("sign", sign);
            Long memberId = null;
            try {
                //返回客户端支付信息
                String notifyInfo = JSON.toJSONString(params);
                AlipayNotifyVo alipayNotifyVo = JSON.parseObject(notifyInfo, AlipayNotifyVo.class);
                alipayNotifyVo.setNotify_url(url);

                //3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
                //4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略
                if (!alipayNotifyVo.getSeller_id().equals(aliMchid)
                        || !alipayNotifyVo.getApp_id().equals(aliAppid)) {
                    logger.error("alipayNotify:seller_id或app_id有误:{},{}", alipayNotifyVo.getSeller_id(), alipayNotifyVo.getApp_id());
                    return FAIL;
                }

                logger.info("alipayNotify接口SyncNotifyVo:{}", notifyInfo);

                //附加数据
                String passback_params = URLDecoder.decode(params.get("passback_params"), "UTF-8");

                //入账处理
                RechargeOrderExample example = new RechargeOrderExample();
                example.createCriteria().andOutTradeNoEqualTo(alipayNotifyVo.getOut_trade_no());
                RechargeOrder rechargeOrder = rechargeOrderService.selectByExample(example).stream().findFirst().orElseGet(() -> null);//获取微信充值订单
                if (!"1".equals(rechargeOrder.getStatus())) {//支付状态 未支付
                    //memberID验证
                    if (!passback_params.equals(String.valueOf(rechargeOrder.getMemberId()))) {
                        logger.error("入帐会员ID不一致:{},:{}", alipayNotifyVo.getOut_trade_no(), passback_params + ":" + rechargeOrder.getMemberId());
                        return FAIL;
                    }

                    //金额验证1:buyer_pay_amount 用户在交易中支付的金额，如果支付通道做补贴活动，客户实际支付的金额会少于商品金额
                    if (!String.valueOf(rechargeOrder.getFactAmount()).equals(String.valueOf(alipayNotifyVo.getBuyer_pay_amount()))) {
                        logger.error("用户在交易中支付的金额跟支付金额不一致:{},:{}", alipayNotifyVo.getOut_trade_no(),
                                rechargeOrder.getFactAmount() + ":" + alipayNotifyVo.getBuyer_pay_amount());
                        //return FAIL;
                    }

                    //金额验证2:receipt_amount 商家在交易中实际收到的款项
                    if (!String.valueOf(rechargeOrder.getFactAmount()).equals(String.valueOf(alipayNotifyVo.getReceipt_amount()))) {
                        logger.error("商家在交易中实际收到的款项跟支付金额不一致:{},:{}", alipayNotifyVo.getOut_trade_no(), rechargeOrder.getFactAmount() + ":" + alipayNotifyVo.getReceipt_amount());
                        return FAIL;
                    }

                    memberId = rechargeOrder.getMemberId();
                    //payType:交易方式(0:微信,1:支付宝)
//                    rechargeOrder = rechargeOrderService.updateRechargeOrderFinish(rechargeOrder.getRechargeOrderId(), "1", new BigDecimal(amount),
//                            trade_no);
                    boolean flag = rechargeOrderService.updateRechargeOrder(rechargeOrder.getRechargeOrderId(), "1", trade_no);

                    if (flag) {
                        logger.info("****支付宝支付回调处理成功！****:{},充值:{},实付:{}", alipayNotifyVo.getOut_trade_no(), rechargeOrder.getAmount(),
                                rechargeOrder.getFactAmount());
                        return SUCCESS;
                    } else {
                        return FAIL;
                    }
                } else {
                    //如果已入帐
                    logger.info("支付宝支付回调已入帐:{},:{}", alipayNotifyVo.getOut_trade_no(), rechargeOrder.getAmount());
                    return SUCCESS;
                }
            } catch (Exception e) {
                logger.error("支付宝入账处理错误", e);
                return FAIL;
            } finally {
                //最后下发余额更新指令
                if (memberId != null) {
                    chargerClientRouter.sendUserAccount(memberId, null);
                }
            }
        } else {
            logger.info("alipayNotify:trade_status标记不为TRADE_SUCCESS:{},:{}", params.get("out_trade_no"), params.get("trade_status"));
            return FAIL;
        }
    }
}